package com.jzt.gateway.service;

import com.jzt.gateway.config.AuthConfig;
import com.jzt.gateway.config.ServersConfig;
import com.jzt.gateway.dao.AccountDao;
import com.jzt.gateway.dao.AccountRegisterDao;
import com.jzt.gateway.dao.UnionUserDao;
import com.jzt.gateway.dto.request.*;
import com.jzt.gateway.entity.Account;
import com.jzt.gateway.entity.AccountRegister;
import com.jzt.gateway.entity.UnionUser;
import com.jzt.gateway.model.AccountInfo;
import com.jzt.gateway.utils.CryptoUtils;
import com.jzt.gateway.utils.DateTimeUtils;
import com.jzt.gateway.utils.EncodeDecodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * 作者：lizw <br/>
 * 创建时间：2020/06/18 14:17 <br/>
 */
@Service
@Slf4j
public class LoginService {

    @Autowired
    private ServersConfig serversConfig;
    @Autowired
    private UnionUserDao unionUserDao;
    @Autowired
    private AccountDao accountDao;
    @Autowired
    private AccountRegisterDao accountRegisterDao;

    /**
     * 验证登录信息，验证失败返回null
     */
    @Transactional(readOnly = true)
    public AccountInfo authentication(AuthConfig authConfig, AbstractLogin loginInfo) {
        AccountInfo accountInfo = loadAccountInfo(authConfig, loginInfo);
        if (accountInfo == null) {
            return null;
        }
        if (!check(accountInfo, loginInfo)) {
            return null;
        }
        return accountInfo;
    }

    protected AccountInfo loadAccountInfo(AuthConfig authConfig, AbstractLogin loginInfo) {
        final String platform = authConfig.getRouteId();
        UnionUser unionUser = null;
        Account account = null;
        if (loginInfo instanceof LoginNamePassword) {
            LoginNamePassword loginNamePassword = (LoginNamePassword) loginInfo;
            if (loginInfo.isUseUnionUser()) {
                unionUser = unionUserDao.getByOneId(loginNamePassword.getName());
            } else {
                account = accountDao.getByLoginName(platform, loginNamePassword.getName());
            }
        } else if (loginInfo instanceof TelephoneValidateCode) {
            TelephoneValidateCode telephoneValidateCode = (TelephoneValidateCode) loginInfo;
            if (loginInfo.isUseUnionUser()) {
                unionUser = unionUserDao.getByTelephone(telephoneValidateCode.getTelephone());
            } else {
                account = accountDao.getByTelephone(platform, telephoneValidateCode.getTelephone());
            }
        } else if (loginInfo instanceof AppScanCode) {
            AppScanCode appScanCode = (AppScanCode) loginInfo;
            // TODO 扫描
        } else if (loginInfo instanceof WechatSmallProgram) {
            WechatSmallProgram wechatSmallProgram = (WechatSmallProgram) loginInfo;
            // TODO 微信
        }
        if (unionUser == null && account != null) {
            unionUser = unionUserDao.getByOneId(account.getOneId());
        }
        if (unionUser != null && account == null) {
            account = accountDao.getByOneId(platform, unionUser.getOneId());
        }
        if (unionUser == null || account == null) {
            return null;
        }
        if (account.getUserAgentId() == null) {
            return null;
        }
        AccountInfo accountInfo = new AccountInfo();
        accountInfo.setOneId(unionUser.getOneId());
        accountInfo.setUserAgentId(account.getUserAgentId());
        accountInfo.setPlatform(account.getPlatform());
        accountInfo.setLoginName(account.getLoginName());
        if (loginInfo.isUseUnionUser()) {
            accountInfo.setLoginPwd(unionUser.getLoginPwd());
        } else {
            accountInfo.setLoginPwd(account.getLoginPwd());
        }
        // 密码解密
        if (StringUtils.isNotBlank(accountInfo.getLoginPwd())) {
            final byte[] key = EncodeDecodeUtils.decodeHex(serversConfig.getPasswordSecretKey());
            final byte[] iv = EncodeDecodeUtils.decodeHex(serversConfig.getPasswordSecretIv());
            final byte[] data = EncodeDecodeUtils.decodeBase64(accountInfo.getLoginPwd());
            accountInfo.setLoginPwd(CryptoUtils.aesDecrypt(data, key, iv));
        }
        return accountInfo;
    }

    protected boolean check(AccountInfo accountInfo, AbstractLogin loginInfo) {
        if (loginInfo instanceof LoginNamePassword) {
            // 用户名密码验证
            return Objects.equals(accountInfo.getLoginPwd(), ((LoginNamePassword) loginInfo).getPassword());
        } else if (loginInfo instanceof TelephoneValidateCode) {
            // TODO 手机验证码
        } else if (loginInfo instanceof AppScanCode) {
            AppScanCode appScanCode = (AppScanCode) loginInfo;
            // TODO 扫描
        } else if (loginInfo instanceof WechatSmallProgram) {
            WechatSmallProgram wechatSmallProgram = (WechatSmallProgram) loginInfo;
            // TODO 微信
        }
        return true;
    }

    /**
     * 查询需要重试注册的账号
     */
    @Transactional(readOnly = true)
    public List<AccountRegister> findNeedRetry() {
        return accountRegisterDao.findNeedRetry(new Date(), 1000);
    }

    /**
     * 重试策略 Map<次数, 时间>
     */
    private static final Map<Integer, Integer> RetryTimeMap = new LinkedHashMap<Integer, Integer>() {{
        put(5, 1000 * 3);
        put(20, 1000 * 20);
        put(Integer.MAX_VALUE, 1000 * 60 * 5);
    }};

    /**
     * 增加重试次数
     */
    @Transactional
    public void addRetryCount(AccountRegister accountRegister) {
        AccountRegister update = new AccountRegister();
        update.setId(accountRegister.getId());
        if (accountRegister.getRetryCount() == null) {
            update.setRetryCount(1);
        } else {
            update.setRetryCount(accountRegister.getRetryCount() + 1);
        }
        update.setLastRetryTime(new Date());
        int preCount = 0;
        for (Map.Entry<Integer, Integer> entry : RetryTimeMap.entrySet()) {
            int count = entry.getKey();
            if (preCount < update.getRetryCount() && update.getRetryCount() < count) {

                update.setNextRetryTime(DateTimeUtils.addMilliseconds(new Date(), entry.getValue()));
                break;
            }
            preCount = count;
        }
        update.setHttpRes(accountRegister.getHttpRes());
        accountRegisterDao.updateById(update);
    }

    /**
     * 重试成功
     */
    @Transactional
    public void registerRetrySuccess(AccountRegister accountRegister) {
        AccountRegister update = new AccountRegister();
        update.setId(accountRegister.getId());
        update.setUserAgentId(accountRegister.getUserAgentId());
        update.setHttpRes(accountRegister.getHttpRes());
        accountRegisterDao.updateById(update);
        accountDao.setUserAgentId(accountRegister.getOneId(), accountRegister.getUserAgentId());
    }
}
